<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace LDL\Twspider\C;

use function PHPSTORM_META\type;
use SuperClosure\Exception\SuperClosureException;
use SuperClosure\Serializer;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use VDB\Spider\Event\SpiderEvents;
use VDB\Spider\Stackable;
use LDL\Twspider\C\StackArray;

/**
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
 *
 * Listeners are registered on the manager and events are dispatched through the
 * manager.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 * @property Serializer serializer
 */
class EventDispatcher extends Stackable
    implements EventDispatcherInterface
{
    private $listeners;//= array();
    private $sorted;//=array();

    function __construct()
    {
        $this->listeners = new Stackable();
        $this->sorted = new Stackable();
    }

    public function dump($key = null)
    {
        if (empty($key))
            var_dump($this->listeners);
        //var_dump($this->listeners[$key]);

    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {

        if (null === $event) {
            $event = new Event();
        }
        if ($listeners = $this->getListeners($eventName)) {
            //var_dump($listeners,$listeners->keys());
            //这是个排序的对像  [k][i-x]  k=sort number ,i-x = object callback
            if ($listeners && !empty($listeners->keys())) {

                $it = $listeners->getIterator();
                $re = null;
                foreach ($it as $ksort => $handlers) {
                    // var_dump($handlers);//[err][2][]=handlers


                    $re = $this->doDispatch($handlers, $eventName, $event, $re);
                }

            }
        }

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    private function fuckbug($eventName)
    {
        if ($eventName == SpiderEvents::SPIDER_CRAWL_ERROR_REQUEST) {
            var_dump($this->listeners[$eventName]);
            //exit(0);

            var_dump("-------");
        }
        //   var_dump($this->sorted[$eventName]);
    }

    public function getListeners($eventName = null)
    {
        if (null !== $eventName) {
            if (!isset($this->listeners[$eventName])) {
                return false;
            }
            $lists = $this->listeners[$eventName];
            $lists->sort();
            return $lists;
        }
        foreach ($this->listeners as $eventName => $eventListeners) {
            //  if (!isset($this->sorted[$eventName])) {
            $this->listeners[$eventName]->sort();
            //  }
        }

        return $this->listeners;
        // return @array_filter($this->sorted);
    }

    /**
     * TODO test  use
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }
        foreach ($this->listeners[$eventName]->getIterator() as $priority => $slisteners) {

            foreach ($slisteners->getIterator() as $kindex => $llistener) {
                if ($llistener == $listener) {
                    return $priority;
                }
            }
        }

    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return (bool)$this->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        //  print_r($eventName);


        // echo "new listender\n";
        $ents = $this->listeners[$eventName];
        if (empty($ents))
            $this->listeners[$eventName] = new StackArray ();

        if (empty($this->listeners[$eventName][$priority])) {
            $this->listeners[$eventName][$priority] = new StackArray ();
        }

        if (!$this->serializer) {
            $this->serializer = new Serializer();
        }

        if (is_array($listener)) {
            $serd = $listener;
        } else if (is_string($listener)) {
            if (function_exists($listener)) {
                $serd = $listener;
            }
        } else if (is_callable($listener)) {
            $serd = $this->serializer->serialize($listener);
        }
        //rd="aaaa";
        // echo "加入start\n";
        $this->listeners[$eventName][$priority][] = $serd;//$listener;
        // echo "加入end\n";
//var_dump($this->listeners[SpiderEvents::SPIDER_CRAWL_POST_REQUEST]);
    }

    /**
     * TODO override
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority => $listeners) {
            if (false !== ($key = array_search($listener, $listeners, true))) {
                unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (is_string($params)) {
                $this->addListener($eventName, array($subscriber, $params));
            } elseif (is_string($params[0])) {
                $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    /**
     * TODO override
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
            if (is_array($params) && is_array($params[0])) {
                foreach ($params as $listener) {
                    $this->removeListener($eventName, array($subscriber, $listener[0]));
                }
            } else {
                $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
            }
        }
    }

    /**
     * Triggers the listeners of an event.
     *
     * This method can be overridden to add functionality that is executed
     * for each listener.
     *
     * @param stackArray $handlers The event listeners
     * @param string $eventName The name of the event to dispatch
     * @param Event $event The event object to pass to the event handlers/listeners
     */
    protected function doDispatch($handlers, $eventName, Event &$event, $lastresult = null)
    {
        $itor = $handlers->getIterator();
        foreach ($itor as $k => $handler) {

            if ($event->isPropagationStopped()) {
                break;
            }

            /*     var_dump($k);
                 var_dump($handler);
                var_dump('---');*/

            if (is_string($handler) && !function_exists($handler)) {
                $handler = $this->serializer->unserialize($handler);
            }
            //传递给事件方法的  Event ,事件方法名,参数上次结果，本类
            //一般函数
            if (is_string($handler)) {
                //normal function
                $lastresult = call_user_func($handler, $event, $eventName, $lastresult, $this);
            } else if (is_callable($handler)) {
                //匿名匿名回调
                $lastresult = $handler($event, $eventName, $lastresult, $this);
            } else {
                //类对像
                // handler Object
                $lastresult = call_user_func(array($handler[0], $handler[1]), $event, $eventName, $lastresult, $this);
            }
        }
        return $lastresult;
    }

    /**
     * Sorts the internal list of listeners for the given event by priority.
     *
     * @param string $eventName The name of the event
     */
    /*private function sortListeners($eventName)
{
    $this->listeners[$eventName]->sort();
    $this->sorted[$eventName] = $this->listeners[$eventName];
}*/
}
